/**
 * Copyright (C) 2010 DustedPixels.com
 */
package com.dustedpixels.cirt.model.cpu.z80;

import com.dustedpixels.cirt.model.core.Circuit;
import com.dustedpixels.cirt.model.core.In;
import com.dustedpixels.cirt.model.core.Out;
import com.dustedpixels.cirt.model.core.Pins;
import com.dustedpixels.cirt.model.core.Script;

/**
 * A set of registers: BC, DE, HL, WZ, IX, IY, PC, SP, plus additional alternate
 * set of exchangable registers: BC', DE', HL', WZ'.
 * 
 * @author Michal Pociecha-Los (micapolos@gmail.com)
 */
@Circuit
public final class Regs {
  public static final int SELECT_BC = 0x00;
  public static final int SELECT_DE = 0x01;
  public static final int SELECT_HL = 0x02;
  public static final int SELECT_WZ = 0x03;
  public static final int SELECT_IX = 0x04;
  public static final int SELECT_IY = 0x05;
  public static final int SELECT_PC = 0x06;
  public static final int SELECT_SP = 0x07;
  
  @In public boolean reset;

  @In @Pins(3) public int regSelectIn;
  @In @Pins(3) public int regSelectOut;
  @In public boolean altSelect;
  
  @In public boolean writeHi;
  @In public boolean writeLo;
  
  @In @Pins(8) public int inHi;
  @In @Pins(8) public int inLo;
    
  @Out @Pins(8) public int outHi;
  @Out @Pins(8) public int outLo;
  
  private final int[] generalPurposeRegs1 = new int[8];
  private final int[] generalPurposeRegs2 = new int[8];
  private final int[] specialPurposeRegs = new int[8];
  
  @Script
  public void update() {
    if (reset) {
      for (int i = 0; i < 8; i++) {
        generalPurposeRegs1[i] = 0xFF;
        generalPurposeRegs2[i] = 0xFF;
      }
      
      for (int i = 0; i < 4; i++) {
        specialPurposeRegs[i] = 0xFFFF;
      }
    } else {
      // Write
      {
        int[] regs;
        if ((regSelectIn & 0x04) == 0) {
          if (altSelect) {
            regs = generalPurposeRegs2;
          } else {
            regs = generalPurposeRegs1;
          }
        } else {
          regs = specialPurposeRegs;
        }
        
        // Calculate regs array indices.
        int selectHi = regSelectIn << 1;
        int selectLo = selectHi ^ 0x01;
        
        // Write in
        if (writeHi) {
          regs[selectHi] = inHi;          
        }
        
        if (writeLo) {
          regs[selectLo] = inLo;
        }
      }
      
      // Read
      {
        int[] regs;
        if ((regSelectOut & 0x04) == 0) {
          if (altSelect) {
            regs = generalPurposeRegs2;
          } else {
            regs = generalPurposeRegs1;
          }
        } else {
          regs = specialPurposeRegs;
        }
        
        // Calculate regs array indices.
        int selectHi = regSelectOut << 1;
        int selectLo = selectHi ^ 0x01;
        
        outHi = regs[selectHi];
        outLo = regs[selectLo];
      }
    }
  }
}
